(* arithmetique sur les vecteur*)
let ( +| ) (x1, y1, z1) (x2, y2, z2) = (x1 +. x2, y1 +. y2, z1 +. z2)

let ( -| ) (x1, y1, z1) (x2, y2, z2) = (x1 -. x2, y1 -. y2, z1 -. z2)

let ( *| ) alpha (x, y, z) = (alpha *. x, alpha *. y, alpha *. z)

let scal (x1, y1, z1) (x2, y2, z2) = x1 *. x2 +. y1 *.y2 +. z1*.z2

let intvect_of_floatvect (xf, yf, zf) = 
    (int_of_float xf, int_of_float yf, int_of_float zf)
    
let norm v = (1. /. sqrt(scal v v) *| v)

let vect (x1, y1, z1) (x2, y2, z2) =
    (z1 *. y2 -. z2 *. y1, x1 *. z2 -. x2 *. z1, y1 *. x2 -. y2 *. x1)

(* string tools *)
let split str =  Str.split (Str.regexp_string str)

(* class de manipulation de fichier obj *)
class obj_file = 
object(this)
    inherit Graphics.render ()
    val mutable v_tab = [||] 
    val mutable vn_tab = [||]
    val mutable f_tab = [||]
   
    method get_v_tab = v_tab
    method get_vn_tab = vn_tab
    method get_f_tab = f_tab
    method private parse_surface str =
        let lst_to_triplet = function
            |val1::[] -> ((try int_of_string val1 with _ -> -1),-1,-1)
            |val1::val2::[] -> ((try int_of_string val1 with _ -> -1), 
                                -1, 
                                (try int_of_string val2 with _ -> -1))
            |val1::val2::val3::[] ->((try int_of_string val1 with _ -> -1),
                                    (try int_of_string val2 with _ -> -1),
                                    (try int_of_string val3 with _ -> -1)) 
            |_ -> (-1,-1,-1)
        in
        lst_to_triplet (split "/" str)

    method private parse_file file=
        let v_list = ref [] in
        let vn_list = ref [] in
        let f_list = ref [] in
        let parse_line line = 
            let rec remove_empty_str = function 
                |[] -> []
                |h::t when h = "" -> remove_empty_str t
                |h::t -> h::(remove_empty_str t)
            in
            match remove_empty_str (split " " line) with
            |"f" :: val1 :: val2 :: val3 :: [] -> f_list := (this#parse_surface
            val1,this#parse_surface val2,this#parse_surface val3)::!f_list
            |"v" :: val1 :: val2 :: val3 :: [] -> v_list := (float_of_string
            val1,float_of_string val2,float_of_string val3)::!v_list
            |"vn":: val1 :: val2 :: val3 :: [] -> vn_list := 
                ((float_of_string val1),
                (float_of_string val2),
                (float_of_string val3))::!vn_list
            |l ->()
        in
        let rec aux file = 
            match try input_line file with _ -> "EOF" with
            |"EOF" -> f_tab <- Array.of_list (List.rev !f_list);
                      v_tab <- Array.of_list (List.rev !v_list);
                      vn_tab <- Array.of_list (List.rev !vn_list);
                        ()
        |line -> parse_line line;aux file
        in
        aux file

    method load_from file_name = 
        if Sys.file_exists file_name then
            let file = open_in file_name in
            this#parse_file file;
            close_in file
        else invalid_arg ("File "^file_name^" not found")
        
    method of_triangle_list tr_list =
        let v_list = ref [] in
        let vn_list =ref  [] in
        let compute_vn v1 v2 v3 = norm(vect (v1 -| v2) (v1 -| v3)) in
        let rec aux = function
            |[] -> ()
            |((x1,y1,z1),(x2,y2,z2),(x3,y3,z3))::t ->
                    v_list := (x3,y3,z3)::(x2,y2,z2)::(x1,y1,z1)::!v_list;
                    vn_list := (compute_vn (x1,y1,z1) (x2,y2,z2) (x3,y3,z3))::!vn_list
        in
        aux tr_list;
        v_tab <- Array.of_list !v_list;
        vn_tab <- Array.of_list !vn_list;
        f_tab <- Array.init (Array.length vn_tab) 
                            (function i -> (i,-1,i),(i+1,-1,i),(i+2,-1,i));
    method save_to file_name =
        let write_comment file =
            Printf.fprintf file "# File genreate by cardogen\n#\n";
            Printf.fprintf file "g [%s]\n\n" (Filename.basename file_name)
        in
        let write_v_tab file =
            let write_v file (x,y,z) =
                Printf.fprintf file "v %f %f %f\n" x y z
            in
            Array.iter (write_v file) v_tab
        in
        let write_vn_tab file =
            let write_vn file (x,y,z) =
                Printf.fprintf file "vn %f %f %f\n" x y z
            in
            Array.iter (write_vn file) vn_tab
        in
        let write_f_tab file = 
            let write_f file (i,j,k) =
                let aux file = function
                    |(-1,_,_) -> invalid_arg "face with out vertex"
                    |(v,-1,-1) -> Printf.fprintf file " %i" v
                    |(v,-1,vn) -> Printf.fprintf file " %i//%i" v vn
                    |(v,vt,vn) -> Printf.fprintf file " %i/%i/%i" v vt vn
                in
                Printf.fprintf file "f";
                aux file i; aux file j; aux file k;
                Printf.fprintf file "\n"
            in
            Array.iter (write_f file) f_tab
        in
        let file = open_out file_name in
        write_comment file;
        write_v_tab file;
        write_vn_tab file;
        write_f_tab file
    
    method draw file_de_fer =
        let aux = function
                |(-1,_,_) -> invalid_arg "face with out vertex"
                |(v,-1,-1) -> GlDraw.vertex3 v_tab.(v-1)
                |(v,-1,vn) -> GlDraw.normal3 vn_tab.(vn-1);
                              GlDraw.vertex3 v_tab.(v-1)
                |(v,vt,vn) -> GlDraw.normal3 vn_tab.(vn-1);
                              GlDraw.vertex3 v_tab.(v-1)
        in
        let draw_f (i,j,k) = 
            aux i;
            aux j;
            aux k;
        in
        if not file_de_fer then
            GlDraw.begins `triangles
        else
            GlDraw.begins `line_strip;
        Array.iter draw_f f_tab;
        GlDraw.ends()
        
end
